/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.core.commands.factories;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gef.ui.parts.GraphicalEditor;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.PlatformUI;
import org.eclipse.uml2.uml.ActivityEdge;
import org.eclipse.uml2.uml.ActivityNode;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.ClassifierTemplateParameter;
import org.eclipse.uml2.uml.Deployment;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Event;
import org.eclipse.uml2.uml.ExecutionSpecification;
import org.eclipse.uml2.uml.Extension;
import org.eclipse.uml2.uml.Interaction;
import org.eclipse.uml2.uml.Lifeline;
import org.eclipse.uml2.uml.Message;
import org.eclipse.uml2.uml.MessageEnd;
import org.eclipse.uml2.uml.MessageOccurrenceSpecification;
import org.eclipse.uml2.uml.OccurrenceSpecification;
import org.eclipse.uml2.uml.ParameterableElement;
import org.eclipse.uml2.uml.Relationship;
import org.eclipse.uml2.uml.TemplateBinding;
import org.eclipse.uml2.uml.TemplateParameter;
import org.eclipse.uml2.uml.TemplateParameterSubstitution;
import org.eclipse.uml2.uml.TemplateSignature;
import org.eclipse.uml2.uml.TemplateableElement;
import org.eclipse.uml2.uml.UMLPackage;

import com.cea.papyrus.core.commands.RemoveCommand;
import com.cea.papyrus.core.commands.association.DeleteAssociationFromModelCommand;
import com.cea.papyrus.core.commands.extension.DeleteExtensionFromModelCommand;
import com.cea.papyrus.core.commands.template.DeleteElementOwnedByATemplateParameterFromModelCommand;
import com.cea.papyrus.core.commands.template.DeleteElementOwnedByATemplateParameterSubstitutionFromModelCommand;
import com.cea.papyrus.core.commands.template.DeleteReferenceFromTemplateParameterCommand;
import com.cea.papyrus.core.commands.template.DeleteReferencedTemplateParameterCommand;
import com.cea.papyrus.core.commands.template.DeleteTemplateParameterSubstitutionCommand;
import com.cea.papyrus.core.editor.ModelManager;
import com.cea.papyrus.core.editor.MultiDiagramEditor;
import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;

//TODO: Auto-generated Javadoc
/**
 * 
 * 
 * @author Remi SCHNEKENBURGER
 * @author Patrick TESSIER
 */
public class UMLDi2ElementDeleteFactory {

	// make private to force singleton use
	/**
	 * 
	 */
	protected UMLDi2ElementDeleteFactory() {

	}

	/**
	 * Singleton instance of the factory.
	 */
	public static UMLDi2ElementDeleteFactory eINSTANCE = new UMLDi2ElementDeleteFactory();

	/**
	 * Update the model manager.
	 * 
	 * @return 
	 */
	protected ModelManager getModelManager(){
		//Darpan
		IEditorPart ie = (IEditorPart) 
					PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor();
		if (ie instanceof MultiDiagramEditor) {
			MultiDiagramEditor mde = (MultiDiagramEditor) ie;
			return mde.getModelManager();
		} else {
			return (ModelManager) ie.getAdapter(ModelManager.class);
		}
	}

	protected AdapterFactoryEditingDomain getEditingDomain() {
		return getModelManager().getDiResourceSet().getEditingDomain();
	}



	/**
	 * Creates the command that deletes a package and all its graphical representations.
	 * 
	 * @param elt the element to delete
	 * 
	 * @return the Compoundcommand that deletes the package
	 */
	@SuppressWarnings("unchecked")
	public Command createCommand(Element elt) {
		CompoundCommand command = new CompoundCommand();
		// when the owner of an element to delete is a TemplateParameter,
		// the switch(eClass.getClassifierID()) can not be used
		if (elt.getOwner() instanceof TemplateParameter) {
			deleteRelatedRelationships(elt, command) ;
			command.add(new DeleteElementOwnedByATemplateParameterFromModelCommand((ParameterableElement)elt)) ;
		}
		else if (elt.getOwner() instanceof TemplateParameterSubstitution) {
			deleteRelatedRelationships(elt, command) ;
			command.add(new DeleteElementOwnedByATemplateParameterSubstitutionFromModelCommand((ParameterableElement)elt)) ;
		}
		else if (elt instanceof TemplateParameterSubstitution) {
			command.add(new DeleteTemplateParameterSubstitutionCommand((TemplateParameterSubstitution)elt)) ;
		}
		else {
			EClass eClass = elt.eClass();
			switch(eClass.getClassifierID()) {
			// Each deletion of relationship is specific
			case UMLPackage.ASSOCIATION:
				deleteRelatedRelationships(elt, command);
				command.add(new DeleteAssociationFromModelCommand((Association)elt));
				break;
			case UMLPackage.EXTENSION:
				deleteRelatedRelationships(elt, command);
				command.add(new DeleteExtensionFromModelCommand((Extension)elt));
				break;
			case UMLPackage.ARTIFACT:
			case UMLPackage.DEPLOYMENT_SPECIFICATION:
			case UMLPackage.INSTANCE_SPECIFICATION:
				deleteDeployedArtifactRelatedRelationships(elt, command);
				//command.add(new UMLElementDeleteCommand(elt, elt.getOwner()));
				command.add(new RemoveCommand(elt, getModelManager()));
				break;
			case UMLPackage.BEHAVIOR_EXECUTION_SPECIFICATION:
				deleteRelatedRelationships(elt, command); //??
				command.add(new RemoveCommand(elt, getModelManager()));
				// Delete ExecutionOccurenceSpecification after the ExecutionSpecification
				// makes the Occurences be restored first in case of "redo"
				// This is important as the ExecutionSpecificationEditPart needs them during
				// activation
				deleteOccurenceSpecification((ExecutionSpecification) elt, command);
				break;
			case UMLPackage.ACTION_EXECUTION_SPECIFICATION:
				deleteRelatedRelationships(elt, command); //??
				command.add(new RemoveCommand(elt, getModelManager()));
				// Delete ExecutionOccurenceSpecification after the ExecutionSpecification
				// makes the Occurences be restored first in case of "redo"
				// This is important as the ExecutionSpecificationEditPart needs them during
				// activation
				deleteOccurenceSpecification((ExecutionSpecification) elt, command);
				break;
			case UMLPackage.MESSAGE:
				deleteRelatedRelationships(elt, command);
				command.add(new RemoveCommand(elt, getModelManager()));
				// Delete ExecutionOccurenceSpecification after the ExecutionSpecification
				// makes the Occurences be restored first in case of "redo"
				// This is important as the ExecutionSpecificationEditPart needs them during
				// activation
				deleteOccurenceSpecification((Message) elt, command);
				break;
			case UMLPackage.EXECUTION_OCCURRENCE_SPECIFICATION:
				deleteRelatedRelationships(elt, command); //??
				command.add(new RemoveCommand(elt, getModelManager()));
				deleteOccurenceSpecificationEvent((OccurrenceSpecification) elt, command);
				break;
			case UMLPackage.MESSAGE_OCCURRENCE_SPECIFICATION:
				deleteRelatedRelationships(elt, command); //??
				command.add(new RemoveCommand(elt, getModelManager()));
				deleteOccurenceSpecificationEvent((OccurrenceSpecification) elt, command);
				break;
			case UMLPackage.OCCURRENCE_SPECIFICATION:
				deleteRelatedRelationships(elt, command); //??
				command.add(new RemoveCommand(elt, getModelManager()));
				deleteOccurenceSpecificationEvent((OccurrenceSpecification) elt, command);
				break;
			case UMLPackage.LIFELINE:
				deleteRelatedRelationships(elt, command);
				command.add(new RemoveCommand(elt, getModelManager()));				
				break;
			case UMLPackage.INTERACTION:
				deleteRelatedRelationships(elt, command);
				deleteLifelines((Interaction) elt, command);
				command.add(new RemoveCommand(elt, getModelManager()));				
				break;
			default:
				deleteRelatedRelationships(elt, command);
			//deleteRelatedSubGraphElements(elt, command);
			command.add(new RemoveCommand(elt, getModelManager()));
			//command.add(new UMLElementDeleteCommand(elt, elt.getOwner()));
			break;
			}
		}
		return command;
	}

	public Command createDeleteTemplateParameter(TemplateParameter elt, TemplateSignature context) {
		if (elt.getSignature() == context) {// the template parameter to delete is owned by the context signature (i.e. it is not referenced)
			return createCommand(elt) ;
		}
		//TODO treatment or referenced parameters
		CompoundCommand command = new CompoundCommand();
		command.add(new DeleteReferencedTemplateParameterCommand(elt, context)) ;
		deleteRelatedTemplateParameterGraphElements(elt, context, command) ;
		return command ;
	}


	/**
	 * Delete related graph elements.
	 * 
	 * @param command the command
	 * @param elt the elt
	 */
	protected void deleteRelatedTemplateParameterGraphElements(TemplateParameter elt, TemplateSignature context, CompoundCommand command) {
		// Delete from diagrams
		Iterator<GraphElement> graphElements = getModelManager().lookForGraphElement(elt, new ArrayList<GraphElement>()).iterator();
		Collection<GraphElement> toDelete = new Vector<GraphElement>() ;
		while(graphElements.hasNext()) {
			GraphElement ge = graphElements.next();
			Element templateSignature = ((Uml1SemanticModelBridge)((GraphNode)ge.getContainer().getContainer()).getSemanticModel()).getElement() ;
			if (templateSignature == context)
				toDelete.add(ge) ;
//			if(elt instanceof Region)
//			command.add(new DeleteRegionNodeCommand((GraphNode)ge));
//			else
//			command.add(DiagramElementDeleteCommandFactory.eINSTANCE.createCommand(ge));
		}
		for (Iterator<GraphElement> i=toDelete.iterator() ; i.hasNext() ; )
			command.add(DiagramElementDeleteCommandFactory.eINSTANCE.createCommand(i.next()));
	}

	/**
	 * Delete related relationships.
	 * 
	 * @param command the command
	 * @param elt the elt
	 */
	protected void deleteRelatedRelationships(Element elt, CompoundCommand command) {
		// Delete each UML element related (relationships for example)
		Iterator<Relationship> rIterator = elt.getRelationships().iterator();
		while(rIterator.hasNext()) {
			Relationship relationship = rIterator.next();
			command.add(createCommand(relationship));
		}

		if( elt instanceof ActivityNode){
			Iterator<ActivityEdge> iterator = ((ActivityNode)elt).getIncomings().iterator();
			while(iterator.hasNext()) {
				ActivityEdge relationship = iterator.next();
				command.add(createCommand(relationship));
			}
		}
		if( elt instanceof ActivityNode){
			Iterator<ActivityEdge> iterator = ((ActivityNode)elt).getOutgoings().iterator();
			while(iterator.hasNext()) {
				ActivityEdge relationship = iterator.next();
				command.add(createCommand(relationship));
			}
		}
		if (elt instanceof TemplateParameter) {		
			if (elt.getModel() != null) {
				// all related template parameter substitution must also be suppressed
				List<TemplateBinding> allTemplateBindings = LookForElement.getAllTemplateBinding(elt.getModel()) ;
				for (Iterator<TemplateBinding> i = allTemplateBindings.iterator() ; i.hasNext() ; ) {
					TemplateBinding current = i.next() ;
					for (Iterator<TemplateParameterSubstitution> j = current.getParameterSubstitutions().iterator() ; j.hasNext() ; ) {
						TemplateParameterSubstitution currentSubstitution = j.next() ;
						if (currentSubstitution.getFormal() == elt) {
							command.add(createCommand(currentSubstitution)) ;
						}
					}
				}
			}
		}
		if (elt instanceof ParameterableElement) {
			if (elt.getModel() != null) {
				// all references in templates that reference this parameterable element must be suppressed
				List<TemplateableElement> allTemplates = LookForElement.getAllTemplates(elt.getModel()) ;
				for (Iterator<TemplateableElement> i = allTemplates.iterator() ; i.hasNext() ; ) {
					TemplateableElement current = i.next() ; 
					for (Iterator<TemplateParameter> j = current.getOwnedTemplateSignature().getParameters().iterator() ; j.hasNext() ; ) {
						TemplateParameter currentParameter = j.next() ;
						if (currentParameter.getDefault() == elt ||
								currentParameter.getParameteredElement() == elt) {
							command.add(new DeleteReferenceFromTemplateParameterCommand(currentParameter, (ParameterableElement)elt)) ;
							if (currentParameter instanceof ClassifierTemplateParameter) {
								if (((ClassifierTemplateParameter)currentParameter).getConstrainingClassifier() == elt)
									command.add(new DeleteReferenceFromTemplateParameterCommand(currentParameter, (ParameterableElement)elt)) ;
							}
						}
						else if (currentParameter instanceof ClassifierTemplateParameter) {
							if (((ClassifierTemplateParameter)currentParameter).getConstrainingClassifier() == elt)
								command.add(new DeleteReferenceFromTemplateParameterCommand(currentParameter, (ParameterableElement)elt)) ;
						}
					}
				}
			}
		}
	}

	/**
	 * Delete related relationships.
	 * 
	 * @param command the command
	 * @param elt the elt
	 */
	protected void deleteDeployedArtifactRelatedRelationships(Element elt, CompoundCommand command) {
		// Delete each UML element related (relationships for example)
		Iterator<Relationship> rIterator = elt.getRelationships().iterator();
		while(rIterator.hasNext()) {
			Relationship relationship = rIterator.next();
			if (!(relationship instanceof Deployment)){
				command.add(createCommand(relationship));
			}
//			else {
//			command.add(new DeconnectDeployedArtifactFromModelCommand((DeployedArtifact)elt,(Deployment)relationship));
//			}
		}
	}

	/**
	 * Delete the Start/Finish {@link OccurrenceSpecification} of the
	 * {@link ExecutionSpecification}
	 * 
	 * @param execution the {@link ExecutionSpecification}
	 * @param command the command 
	 */
	protected void deleteOccurenceSpecification(ExecutionSpecification execution, CompoundCommand command) {

		OccurrenceSpecification start  = execution.getStart();
		OccurrenceSpecification finish = execution.getFinish();

		if (start != null) {
			command.add(
					UMLDi2ElementDeleteFactory.eINSTANCE.createCommand(start));
		}
		if (finish != null) {
			command.add(
					UMLDi2ElementDeleteFactory.eINSTANCE.createCommand(finish));	
		}
	}

	/**
	 * Delete the Send/Received {@link MessageOccurrenceSpecification} of the
	 * {@link Message}
	 * 
	 * @param message the {@link Message} 
	 * @param command the command
	 */
	protected void deleteOccurenceSpecification(Message message, CompoundCommand command) {

		MessageEnd sendMsgEnd  = message.getSendEvent();
		MessageEnd recvMsgEnd = message.getReceiveEvent();

		if (sendMsgEnd != null) {
			command.add(
					UMLDi2ElementDeleteFactory.eINSTANCE.createCommand(sendMsgEnd));
		}
		if (recvMsgEnd != null) {
			command.add(
					UMLDi2ElementDeleteFactory.eINSTANCE.createCommand(recvMsgEnd));	
		}
	}

	/**
	 * Delete the Event of the {@link OccurrenceSpecification}
	 * 
	 * @param occurence the {@link OccurrenceSpecification} 
	 * @param command the command
	 */
	protected void deleteOccurenceSpecificationEvent(OccurrenceSpecification occurence, CompoundCommand command) {

		Event event  = occurence.getEvent();				
		if (event != null) {
			command.add(new RemoveCommand(event, getModelManager()));
		}

	}

	/**
	 * Delete the Start/Finish {@link OccurrenceSpecification} of the
	 * {@link ExecutionSpecification}
	 * 
	 * @param command the command
	 * @param elt the {@link ExecutionSpecification} 
	 */
	protected void deleteLifelines(Interaction interaction, CompoundCommand command) {

		Iterator<Lifeline> i = interaction.getLifelines().iterator();
		while(i.hasNext()) {
			command.add(
					UMLDi2ElementDeleteFactory.eINSTANCE.createCommand(i.next()));

		}	
	}
}